package com.weifly.weistock.module.stockdata;

import com.weifly.weistock.bo.MergeRecordResult;
import com.weifly.weistock.core.common.StockException;
import com.weifly.weistock.core.constant.StockCatalogEnum;
import com.weifly.weistock.core.market.bo.StockDayBO;
import com.weifly.weistock.core.market.bo.StockDividendBO;
import com.weifly.weistock.core.market.bo.StockKLineBO;
import com.weifly.weistock.core.util.WeistockUtils;
import com.weifly.weistock.module.stockdata.bo.GetDayListRequest;
import org.apache.commons.lang3.StringUtils;

import javax.servlet.http.HttpServletRequest;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * converter
 *
 * @author weifly
 * Create at 2021/6/11
 */
public class StockDataConverter {

    public static Map<String, Object> convertToStockDataDTO(StockKLineBO stockKLineBO) {
        Map<String, Object> info = new HashMap<>();
        info.put("stockCode", stockKLineBO.getStockCode());
        info.put("stockName", StringUtils.isBlank(stockKLineBO.getStockName()) ? "" : stockKLineBO.getStockName());
        List<StockDayBO> dayList = stockKLineBO.getDayList();
        if (dayList.size() > 0) {
            StockDayBO startDay = dayList.get(0);
            info.put("startDay", startDay.getDay());
            info.put("startValue", startDay.getClose());
            StockDayBO endDay = dayList.get(dayList.size() - 1);
            info.put("endDay", endDay.getDay());
            info.put("endValue", endDay.getClose());
        }
        return info;
    }

    public static Map<String, Object> convertToStockDayDTO(StockDayBO stockDayBO) {
        Map<String, Object> info = new HashMap<>();
        info.put("day", stockDayBO.getDay());
        info.put("close", stockDayBO.getClose());
        return info;
    }

    public static GetDayListRequest convertToGetDayListRequest(HttpServletRequest request, int limit) {
        String stockCode = request.getParameter("stockCode");
        if (StringUtils.isBlank(stockCode)) {
            throw new StockException("缺少stockCode参数");
        }
        GetDayListRequest queryRequest = new GetDayListRequest();
        queryRequest.setStockCode(stockCode);
        queryRequest.setDate(request.getParameter("date")); // 发生日期
        queryRequest.setLimit(limit + 1);
        return queryRequest;
    }

    /**
     * 合并dayList
     */
    public static void mergeDayList(StockKLineBO source, StockKLineBO target, MergeRecordResult mergeRecordResult) {
        mergeRecordResult.setTotal(source.getDayList().size());
        Map<String, StockDayBO> targetDayMap = target.getDayList().stream().collect(Collectors.toMap(StockDayBO::getDay, Function.identity()));
        for (StockDayBO stockDayBO : source.getDayList()) {
            if (targetDayMap.containsKey(stockDayBO.getDay())) {
                mergeRecordResult.setUpdate(mergeRecordResult.getUpdate() + 1);
            } else {
                mergeRecordResult.setInsert(mergeRecordResult.getInsert() + 1);
            }
            targetDayMap.put(stockDayBO.getDay(), stockDayBO);
        }
        target.getDayList().clear();
        target.getDayList().addAll(targetDayMap.values());
        target.getDayList().sort((day1, day2) -> day1.getDay().compareTo(day2.getDay()));
    }

    /**
     * 加载数据列表，用于chart显示
     */
    public static List<StockDayBO> loadStockDayList(StockKLineBO stockKLineBO, String lowDay) {
        LinkedList<StockDayBO> dayList = new LinkedList<>();
        if (StringUtils.isBlank(lowDay)) {
            return dayList;
        }
        if (stockKLineBO.getDayList() == null) {
            return dayList;
        }
        Iterator<StockDayBO> dayIter = stockKLineBO.getDayList().iterator();
        while (dayIter.hasNext()) {
            StockDayBO dayBO = dayIter.next();
            if (lowDay.compareTo(dayBO.getDay()) <= 0) {
                dayList.add(dayBO);
            }
        }
        return dayList;
    }

    /**
     * 获得最小值
     */
    public static double getLow(StockDayBO dayBO) {
        if (dayBO.getLow() > 0) {
            return dayBO.getLow();
        }
        if (dayBO.getClose() > 0) {
            return dayBO.getClose();
        }
        return 0;
    }

    /**
     * 计算前复权价格
     */
    public static void calcFrontPrice(StockKLineBO stockKLineBO) {
        Map<String, StockDividendBO> dividendMap = stockKLineBO.getDividendList().stream().collect(Collectors.toMap(StockDividendBO::getDay, Function.identity()));
        int scale = 2;
        if (stockKLineBO.getStockCatalog().equals(StockCatalogEnum.SH_FUND) || stockKLineBO.getStockCatalog().equals(StockCatalogEnum.SZ_FUND)) {
            scale = 3;
        }
        List<StockDayBO> dayList = stockKLineBO.getDayList();
        ListIterator<StockDayBO> dayIterator = dayList.listIterator(dayList.size());
        while (dayIterator.hasPrevious()) {
            StockDayBO targetDay = dayIterator.previous();
            StockDividendBO stockDividend = dividendMap.get(targetDay.getDay());
            if (stockDividend != null) {
                calcStockDividend(dayIterator, stockDividend, scale);
            } else {
                // 清空前复权信息
                targetDay.setFront_open(null);
                targetDay.setFront_high(null);
                targetDay.setFront_low(null);
                targetDay.setFront_close(null);
            }
        }
    }

    private static void calcStockDividend(ListIterator<StockDayBO> dayIterator, StockDividendBO stockDividend, int scale) {
        int dayCount = 40; // 复权前40天
        while (dayIterator.hasPrevious() && dayCount > 0) {
            dayCount--;
            StockDayBO frontDay = dayIterator.previous();
            double frontOpen = frontDay.getOpen();
            double frontHigh = frontDay.getHigh();
            double frontLow = frontDay.getLow();
            double frontClose = frontDay.getClose();
            if (stockDividend.getDividendAmount() != null) {
                double dividendAmount = stockDividend.getDividendAmount() / 10;
                frontOpen = WeistockUtils.subtract(frontOpen, dividendAmount);
                frontHigh = WeistockUtils.subtract(frontHigh, dividendAmount);
                frontLow = WeistockUtils.subtract(frontLow, dividendAmount);
                frontClose = WeistockUtils.subtract(frontClose, dividendAmount);
            }
            if (stockDividend.getDividendStock() != null) {
                int dividendStock = stockDividend.getDividendStock();
                frontOpen = WeistockUtils.multi(frontOpen, (10 / (10 + dividendStock)), scale);
                frontHigh = WeistockUtils.multi(frontHigh, (10 / (10 + dividendStock)), scale);
                frontLow = WeistockUtils.multi(frontLow, (10 / (10 + dividendStock)), scale);
                frontClose = WeistockUtils.multi(frontClose, (10 / (10 + dividendStock)), scale);
            }
            frontDay.setFront_open(frontOpen);
            frontDay.setFront_high(frontHigh);
            frontDay.setFront_low(frontLow);
            frontDay.setFront_close(frontClose);
        }
    }
}
